/**
 * 
 */
package edu.acatlan.unam.client.ui.controlador.lienzo;

//Librerías de la clase
import com.google.gwt.canvas.dom.client.CssColor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

//Librerías del proyecto
import edu.acatlan.unam.client.ui.controlador.CBDisegnadorLienzo;
import edu.acatlan.unam.shared.bdoo.ElementoGrafico;
import edu.acatlan.unam.shared.bdoo.TColumna;
import edu.acatlan.unam.shared.bdoo.TRelacion;
import edu.acatlan.unam.shared.bdoo.TTabla;

/**
 * @author jacobo
 * 
 * Esta clase es el controlador del lienzo. Funciona como puente de comunicación entre el menú
 * del lienzo y el lienzo. Maneja los eventos ocurridos sobre el menú y sobre el lienzo.
 */
public class CLienzo implements ILienzo {
	//Propiedades de la clase
	private CBLienzo retro;
	private CBDisegnadorLienzo cbPrincipal;
	public OpLienzo opcion, estadoBoton;
	public int mouseX, mouseY;				//Posición relativa del ratón en el lienzo 
	private int incX, incY;
	private ElementoGrafico seleccionado;
	private TTabla relaciones[];
	
	/**
	 * Constructor de la clase
	 */
	public CLienzo() {
		opcion = OpLienzo.SELECCIONAR;
		estadoBoton = OpLienzo.MOVIENDO;
		relaciones = new TTabla[2];
	}

	/* (non-Javadoc)
	 * @see edu.acatlan.unam.client.ui.controlador.lienzo.ILienzo#setEleccion(edu.acatlan.unam.client.ui.controlador.lienzo.OpLienzo)
	 */
	public void setEleccion(OpLienzo e) {
		opcion = e;
		if(e == OpLienzo.DESCARGAR)
			actualizar();
	}
	
	/**
	 * @param retro es una interfaz de comunicación con el lienzo
	 */
	public void vincularControl(CBLienzo retro) {
		this.retro = retro;
	}
	
	/**
	 * Esta función actualiza los objetos que se dibujarán en el lienzo
	 */
	public void actualizar() {
		ArrayList<TTabla> tablas = cbPrincipal.getTablas();
		
		switch(this.opcion) {
		case SELECCIONAR:
			switch(estadoBoton) {
			case PRESIONADO:
				boolean elementoEncontrado = false;
				/*
				 * Si se ha presionado sobre el canvas, identificar el elemento y enfocarlo cambiándole
				 * el estilo y la posición.
				 * */
				for(TTabla t : tablas) {
					if(t.estaBajo(mouseX, mouseY)) {
						seleccionado = t;
						elementoEncontrado = true;
						incX = mouseX - seleccionado.getPosX();
						incY = mouseY - seleccionado.getPosY();
						cbPrincipal.ponerEditor(t);
					}
					if(!elementoEncontrado) {
						for(TRelacion r : t.getRelaciones())
							if(r.estaBajo(mouseX, mouseY))
								break;
					}
				}
				break;
			case ARRASTRANDO:
				/*
				 * TODO: Mejorar esta función de arrastrando pensando en que hay que actualizar todas las referencias que lleven
				 * a esa tabla.
				 * */
				if(seleccionado != null && seleccionado.estaBajo(mouseX, mouseY))
					seleccionado.setPosicion(mouseX - incX, mouseY - incY);
				break;
			case MOVIENDO:
				break;
			}
			
			break;
			
		case NUEVA_TABLA:
			if(estadoBoton == OpLienzo.PRESIONADO)
				tablas.add(new TTabla("Nueva_Tabla_" + Integer.toString(1 + tablas.size()), cbPrincipal.getIdEsquema(), mouseX, mouseY));
			break;
			
		case RELACION_1_1:
			crearRelacion(tablas);
			break;
			
		case RELACION_1_N:
			crearRelacion(tablas);
			break;
			
		case RELACION_N_M:
			//TODO: Implementar en una segunda versión
			break;
			
		case SINCRONIZAR:
			//TODO: Hacer una retrollamada al servidor para sincronizar el modelo con JDBC
			break;
			
		case DESCARGAR:
			cbPrincipal.generarScript();
			opcion = OpLienzo.SELECCIONAR;
			break;
			
		case SUPRIMIR:
			//TODO: Eliminar el elemento seleccionado por el mouse (sólo el que esté seleccionado). 
			break;
		default:
		}
		
		pintar();
	}

	/**
	 * Esta función recorre cada una de las tablas en la lista y las pinta
	 * en el contexto del lienzo.
	 */
	private void pintar() {
		ArrayList<TTabla> tablas = cbPrincipal.getTablas();
		/**
		 * Se limpia el buffer trasero con el color del fondo y pintamos un rectángulo con las dimensiones del lienzo.
		 * */
		retro.getContextoLogico().setFillStyle(CssColor.make("rgba(255, 255, 255, 0.6)"));
		retro.getContextoLogico().fillRect(0, 0, retro.getAncho(), retro.getAlto());		//Indicamos la sección del lienzo a pintar en forma de rectángulo
		
		/**
		 * Pintamos en el buffer trasero los objetos necesarios.
		 * */
		retro.getContextoLogico().beginPath();
			for(int i = 0; i < tablas.size(); i++) {
				TTabla t = tablas.get(i);
				t.dibuja(retro.getContextoLogico());
				for(TRelacion r : t.getRelaciones())
					r.dibuja(retro.getContextoLogico());
			}
			
		retro.getContextoLogico().closePath();
		retro.getContextoLogico().stroke();
		retro.getContextoLogico().fill();
		//Dibujamos en el buffer principal aquello que está en el buffer lógico.
		retro.getContextoFrontal().drawImage(retro.getContextoLogico().getCanvas(), 0, 0);
	}

	/**
	 * @param c es el controlador del Diseñador
	 */
	public void vincular(CBDisegnadorLienzo c) {
		this.cbPrincipal = c;
		c.vincular(this);
	}
	
	/**
	 * @param tablas son las tablas del esquema de base de datos
	 */
	private void crearRelacion(ArrayList<TTabla> tablas) {
		if(estadoBoton == OpLienzo.PRESIONADO) {
			OpLienzo o = OpLienzo.P_FUERA_TABLA;
			TTabla t = null;
			//Buscar el objeto que se presionó
			for(TTabla s : tablas)
				if(s.estaBajo(mouseX, mouseY)) {
					t = s;
					o = OpLienzo.P_TABLA;
				}
			
			switch(o) {
			case P_FUERA_TABLA:
				//Liberar recursos
				this.relaciones[0] = null;
				this.relaciones[1] = null;
				this.opcion = OpLienzo.SELECCIONAR;
				this.estadoBoton = OpLienzo.MOVIENDO;
				break;
			case P_TABLA:
				//Agregar la tabla seleccionada para formar las relaciones
				switch(opcion) {
				case RELACION_1_1:
					if(relaciones[0] == null)
						relaciones[0] = t;
					else
						relaciones[1] = t;
					break;
				case RELACION_1_N:
					if(relaciones[1] == null)
						relaciones[1] = t;
					else
						relaciones[0] = t;
					break;
				}
				
				if(relaciones[0] != null && relaciones[1] != null) {
					//Crear la relación
					ArrayList<TColumna> columnas = relaciones[1].getColumnas(), 
							colRel = new ArrayList<TColumna>();
					ArrayList<Long> idCol = new ArrayList<Long>();
					Map<Long, Long> ref = new HashMap<Long, Long>();
					Map<String, String> nomCols = new HashMap<String, String>();
					
					for(TColumna c : columnas)
						if(c.getEsLlave()) {
							colRel.add(new TColumna(c.getNombreCol(), 
									relaciones[0].getIdTabla(), c.getTipoDato(), 
									false, true, false, c.getEsUnico(), true, "", 0));
							nomCols.put(c.getNombreCol(), c.getNombreCol());
							idCol.add(c.getIdColumna());
						}
					
					//TODO: Enviar las columnas colRel al servidor para ser creadas
					
					for(int i = 0; i < colRel.size(); i++) {
						ref.put(colRel.get(i).getIdColumna(), idCol.get(i));
						relaciones[0].getColumnas().add(colRel.get(i));
					}
					
					//Crear la relacion
					TRelacion r = new TRelacion("r-" + relaciones[0].getNombreTb() + "-" + relaciones[1].getNombreTb(), 
							relaciones[0].getIdTabla(), relaciones[1].getIdTabla(), ref);
					r.setPosicion(relaciones[0].getPosX(), relaciones[0].getPosY());
					r.setPosicionFinal(relaciones[1].getPosX(), relaciones[1].getPosY());
					r.setNomColumnas(nomCols);
					r.setNomTablaProp(relaciones[0].getNombreTb());
					r.setNomTablaRef(relaciones[1].getNombreTb());
					
					//TODO: Enviar la relación al servidor para almacenar
					
					relaciones[0].getRelaciones().add(r);
					
					this.relaciones[0] = null;
					this.relaciones[1] = null;
					this.opcion = OpLienzo.SELECCIONAR;
					this.estadoBoton = OpLienzo.SOLTADO;
				}
				break;
			}
		}
	}
}
